package com.atguigu.gmall.product.service.impl;

import com.atguigu.gmall.product.service.TestService;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.redisson.client.RedisClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author mqx
 * @date 2020-7-31 09:03:59
 */
@Service
public class TestServiceImpl implements TestService {


    // 引入操作redis 的客户端工具类
    @Autowired
    private StringRedisTemplate redisTemplate;

    // 使用redisson
    @Autowired
    private RedissonClient redissonClient;


    @Override
    public void testLock() {
        // 获取锁
        // 这个锁的key 集合商品详情设置一个新的key
        String skuId = "37";
        // 设置锁的key
        String lockKey = "lock:"+skuId;
        RLock lock = redissonClient.getLock(lockKey);
        // 加锁
        lock.lock();
        // lock.lock(10,TimeUnit.SECONDS);
        //        try {
        //            boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
        //
        //            try {
        //                if (res){
        //                    // 业务逻辑代码
        //                }
        //            } catch (Exception e) {
        //                e.printStackTrace();
        //            } finally {
        //                // 最终解锁处理！
        //                lock.unlock();
        //            }
        //        } catch (InterruptedException e) {
        //            e.printStackTrace();
        //        }
         /*
            整个过程需要7秒钟！
        1.  获取缓存中对应的num 的数值 在缓存中给num 初始化 0
        2.  判断能否得到num 的数值
            2.1 true 将num 加 1 ,放入缓存。
            2.2 false 直接返回
        */
        String value = redisTemplate.opsForValue().get("num");
        // 没有 数据，直接返回
        if (StringUtils.isEmpty(value)){
            return;
        }
        // 将value 转化为数值
        int num = Integer.parseInt(value);
        // 将加完之后的数据放回缓存！
        redisTemplate.opsForValue().set("num",String.valueOf(++num));
        // 解锁
        lock.unlock();
    }

    /*
    RReadWriteLock rwlock = redisson.getReadWriteLock("anyRWLock");
    // 最常见的使用方法
    rwlock.readLock().lock();
    // 或
    rwlock.writeLock().lock();
     */
    @Override
    public String readLock() {
        // 如果定义读写锁
        // 参数中传递的锁的名称
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("atguigu");
//        RLock rLock = readWriteLock.readLock();
//        rLock.lock(10,TimeUnit.SECONDS);
        // 相当于加锁 10s
        readWriteLock.readLock().lock(10,TimeUnit.SECONDS);
        // 从缓存获取数据
        String msg = redisTemplate.opsForValue().get("msg");
        // 返回读取的内容
        return msg;
    }

    @Override
    public String writeLock() {
        // 参数中传递的锁的名称
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("atguigu");
        // 调用写的方法 加锁 10s
        readWriteLock.writeLock().lock(10,TimeUnit.SECONDS);
        redisTemplate.opsForValue().set("msg",UUID.randomUUID().toString());
        return "写入完成了。。。。。";
    }

//    @Override
//    public void testLock() {
//
//        // 利用setnx 来加锁 setnx lock atguigu
//        // setnx刚好获取到锁，在执行业务逻辑出现异常，导致锁无法释放！
//        // Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "atguigu");
//        // set k1 v1 px 20000 nx
//        // 设置一个UUID 防止误删锁
//        String uuid = UUID.randomUUID().toString();
//        // 这个锁的key 集合商品详情设置一个新的key
//        String skuId = "37";
//        // 设置锁的key
//        String lockKey = "lock:"+skuId;
//        Boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, uuid,2, TimeUnit.SECONDS);
//        // lock=true 上锁成功
//        if (lock){
//             /*
//                整个过程需要7秒钟！
//            1.  获取缓存中对应的num 的数值 在缓存中给num 初始化 0
//            2.  判断能否得到num 的数值
//                2.1 true 将num 加 1 ,放入缓存。
//                2.2 false 直接返回
//            */
//            String value = redisTemplate.opsForValue().get("num");
//            // false 直接返回
//            if (StringUtils.isEmpty(value)){
//                return;
//            }
//            // 将value 转化为数值
//            int num = Integer.parseInt(value);
//            // 将加完之后的数据放回缓存！
//            redisTemplate.opsForValue().set("num",String.valueOf(++num));
//
//            // 将这个锁删除 del
//            // 判断代码执行的uuid 与缓存的uuid 做个比较
//            //            if (uuid.equals(redisTemplate.opsForValue().get("lock"))){
//            //                // 线程一：执行到判断，返回结果是true，还没有执行delete 这个命令。立刻这key 到了过期时间。也就是说没有锁了。
//            //                // 线程二：进来了，重新设置了uuid 和 缓存的值！
//            //                // 此时，我们cpu 又开始执行线程一，那么线程一就会将线程二的锁给删除！
//            //                // 这样删除就不具备了原子性！
//            //                redisTemplate.delete("lock");
//            //            }
//            // 不建议使用del 删除锁！
//            // 创建一个lua 脚本
//            // 这个脚本只在客户端传入的值和键的口令串相匹配时
//            String script="if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
//            // 执行这个lua 脚本
//            DefaultRedisScript defaultRedisScript = new DefaultRedisScript();
//            // 将lua脚本放入对象
//            defaultRedisScript.setScriptText(script);
//            // 设置一个返回值
//            defaultRedisScript.setResultType(Long.class);
//            redisTemplate.execute(defaultRedisScript, Arrays.asList(lockKey),uuid);
//        }else {
//            // 等待
//            try {
//                Thread.sleep(1000);
//                // 睡醒了之后，再次调用
//                testLock();
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//        }
//    }


}
